21. ContentProvider getType() Method

ContentProvider GetType() Method

You might have noticed that there is another required method in the PetProvider that we need to override: the getType(Uri uri) method. The purpose of this method is to return a String that describes the type of the data stored at the input Uri. This String is known as the MIME type, which can also be referred to as content type.

One use case where this functionality is important is if you’re sending an intent with a URI set on the data field. The Android system will check the MIME type of that URI to determine which app component on the device can best handle your request. (If the URI happens to be a content URI, then the system will check with the corresponding ContentProvider to ask for the MIME type using the getType() method.) See further details under the “Data” header in the Building an Intent section of this article.

The Android documentation describes the ContentProvider getType() method in the following way:

Implement this [method] to handle requests for the MIME type of the data at the given URI. The returned MIME type should start with “vnd.android.cursor.item” for a single record, or “vnd.android.cursor.dir/” for multiple items.

Contacts app MIME type examples

The definition may seem extra confusing at first, but let’s look at an example. The ContactsProvider handles many different types of data - contacts, photos, phone numbers, email addresses, etc… Each type of data has a different MIME type. See how the getType() method is implemented in the ContactsProvider.

For a common data type like an image, there are already widespread conventions to use the MIME type String “image/jpeg” or “image/png” (or whatever the image file extension is). However for data specific to an app, it’s possible to define your own custom MIME types.

The Contacts app defined a custom MIME type for a single contact as a constant value in the ContactsContract. Then this MIME type is returned by the getType() method if a Uri refers to a single contact. Notice how the custom MIME type starts with “vnd.android.cursor.item” (as mentioned in the definition above) because it’s a single record. In this case, “single record” means a “single row” in the database table.

  /**
   * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
   * person.
   */
  public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact";

If the URI refers to a whole list of contacts, then the Contacts app uses a different custom MIME type. Notice how the custom MIME type starts with “vnd.android.cursor.dir” (where “dir” is short for “directory”) because the URI can point to multiple records. In this case, multiple records means multiple rows, or multiple contacts in the database.

  /**
   * The MIME type of {@link #CONTENT_URI} providing a directory of
   * people.
   */
  public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact";

Another Explanation of MIME type

Next, I want you read the response to this StackOverflow post which has a pretty good explanation and examples of why MIME types are important to a ContentProvider. (This article on Wikipedia also explains MIME types in more detail.)

Define Custom Pet MIME types

In our app we have essentially two types of URIs. The first is the URI is “content://com.example.android.pets/pets/”, which references the entire pets table. Basically it represents a list of pets. In MIME type terms, this is known as a directory of data. The second URI is “content://com.example.android.pets/pets/#”, which represents a single pet. In MIME type terms, a single row of data is an item of data.

  content://com.example.android.pets/pets → Returns directory MIME type
  content://com.example.android.pets/pets/# → Returns item MIME type

Since the MIME types need to follow a specific format, here are the MIME types for the data in our app. The MIME type strings start with the convention “vnd.android.cursor…”, are followed by the pet content authority, and the path to the data.

Directory MIME type:
vnd.android.cursor.dir/com.example.android.pet/pets

Item MIME type:
vnd.android.cursor.item/com.example.android.pet/pets

Step 1: Declare MIME Type constants in PetContract

To implement this behavior in our PetProvider, we should first declare constants representing the MIME types in our PetContract file, within the PetEntry class. The very subtle difference is in the word after cursor: either “dir” or “item”.

PetEntry.CONTENT_LIST_TYPE
vnd.android.cursor.dir/com.example.android.pet/pets

PetEntry.CONTENT_ITEM_TYPE
vnd.android.cursor.item/com.example.android.pet/pets

Add the below code to your app. You can insert these constants right after where the pet content URI is defined in your file.

In PetContract.java:

      public static final class PetEntry implements BaseColumns {

          …

          /**
           * The MIME type of the {@link #CONTENT_URI} for a list of pets.
           */
          public static final String CONTENT_LIST_TYPE =
                  ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_PETS;

          /**
           * The MIME type of the {@link #CONTENT_URI} for a single pet.
           */
          public static final String CONTENT_ITEM_TYPE =
                  ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_PETS;

          …

You’ll notice that we’re making use of the constants defined in the ContentResolver class: CURSOR_DIR_BASE_TYPE (which maps to the constant "vnd.android.cursor.dir") and CURSOR_ITEM_BASE_TYPE (which maps to the constant “vnd.android.cursor.item”). Hence, add this additional import statement for the ContentResolver class at the top of the PetContract (if it’s not automatically imported already).

In PetContract.java:

  import android.content.ContentResolver;

For the full context of the PetContract.java file, see here.

Step 2: Implement ContentProvider getType() method

Next comes the actual implementation of getType(), where the correct MIME type must be returned for each Uri. Replace this version of the getType() method with the blank one that exists in your PetProvider class now.

UriMatcher PETS case → Return MIME type PetEntry.CONTENT_LIST_TYPE
UriMatcher PET_ID case → Return MIME type PetEntry.CONTENT_ITEM_TYPE

In PetProvider.java:

       @Override
      public String getType(Uri uri) {
          final int match = sUriMatcher.match(uri);
          switch (match) {
              case PETS:
                  return PetEntry.CONTENT_LIST_TYPE;
              case PET_ID:
                  return PetEntry.CONTENT_ITEM_TYPE;
              default:
                  throw new IllegalStateException("Unknown URI " + uri + " with match " + match);
          }
      }

For the full code diff for this code checkpoint, check this link.

That’s the end of building your first ContentProvider, huge congratulations to you! Give yourself a pat on the back and high-five a friend or anybody nearby!